package services

import (
	"gitee.com/siruihuyu/appapi/game/core"
	"github.com/astaxie/beego/logs"
	"strings"
	"strconv"
	"time"
	"net/http"
	"encoding/json"
	"io/ioutil"
	"gitee.com/siruihuyu/appapi/module/idgenerate"
	"sort"
	"fmt"
)

//GetBalance需要的所有参数
type GetBalance struct {
	//用户唯一标识符
	OpenId string `json:"openid"`
	//小程序 appId
	AppId string `json:"appid"`
	//米大师分配的offer_id
	OfferId string `json:"offer_id"`
	//UNIX 时间戳，单位是秒
	TS int64 `json:"ts"`
	//游戏服务器大区id,游戏不分大区则默认zoneId ="1",String类型。如过应用选择支持角色，则角色ID接在分区ID号后用"_"连接。
	ZoneId string `json:"zone_id"`
	//平台 安卓：android
	PF string `json:"pf"`
	//米大师签名
	Sig string `json:"sig"`
	//米大师另一个签名
	MpSig string `json:"mp_sig"`
	//接口调用凭证
	//AccessToken string `json:"access_token"`
}

//GetBalance返回所需要的参数
type GetBalanceReturnSt struct {
	Errcode    int    `json:"errcode"`
	Errmsg     string `json:"errmsg"`
	Balance    int64  `json:"balance"`
	GenBalance int64  `json:"gen_balance"`
	FirstSave  int64  `json:"first_save"`
	SaveAmt    int64  `json:"save_amt"`
	SaveSum    int64  `json:"save_sum"`
	CostSum    int64  `json:"cost_sum"`
	PresentSum int64  `json:"present_sum"`
}

//米大师游戏币查询接口
//在用户成功调用支付后
func MidasGetBalance(openid, sessionKey, gameName string) (*GetBalanceReturnSt, error) {

	accessToken := core.GetAccessToken(gameName)
	wxConf := core.GetWXConf(gameName)
	getBalance := GetBalance{}
	getBalance.AppId = wxConf.AppId
	getBalance.OfferId = wxConf.OfferId
	getBalance.PF = wxConf.PF
	getBalance.OpenId = openid
	getBalance.TS = time.Now().Unix()
	getBalance.ZoneId = "1"
	//getBalance.AccessToken = accessToken

	var sig strings.Builder
	//appid
	sig.WriteString("appid=")
	sig.WriteString(getBalance.AppId)
	//米大师的id
	sig.WriteString("&offer_id=")
	sig.WriteString(getBalance.OfferId)
	//openid
	sig.WriteString("&openid=")
	sig.WriteString(getBalance.OpenId)
	//pf 平台标志
	sig.WriteString("&pf=")
	sig.WriteString(getBalance.PF)
	//时间戳
	sig.WriteString("&ts=")
	sig.WriteString(strconv.FormatInt(getBalance.TS, 10))
	//目前部分区服，所以只有一个服
	sig.WriteString("&zone_id=1")
	//uri+secret
	sig.WriteString("&org_loc=/cgi-bin/midas/sandbox/getbalance&method=POST&secret=")
	sig.WriteString(wxConf.MidasKey)

	//获取哈希
	sigHMacSha256 := core.GetHmacCode(sig.String(), wxConf.MidasKey)
	logs.Info("MidasGetBalance sigHMacSha256 [%s]", sigHMacSha256)

	getBalance.Sig = sigHMacSha256

	var mpSig strings.Builder
	mpSig.WriteString("access_token=")
	//mpSig.WriteString("ACCESSTOKEN")
	mpSig.WriteString(accessToken)

	mpSig.WriteString("&appid=")
	//mpSig.WriteString("wx1234567")
	mpSig.WriteString(getBalance.AppId)

	mpSig.WriteString("&offer_id=")
	//mpSig.WriteString("12345678")
	mpSig.WriteString(getBalance.OfferId)

	mpSig.WriteString("&openid=")
	//mpSig.WriteString("odkx20ENSNa2w5y3g_qOkOvBNM1g")
	mpSig.WriteString(getBalance.OpenId)

	mpSig.WriteString("&pf=")
	//mpSig.WriteString("android")
	mpSig.WriteString(getBalance.PF)

	mpSig.WriteString("&sig=")
	mpSig.WriteString(sigHMacSha256)

	mpSig.WriteString("&ts=")
	//mpSig.WriteString("1507530737")
	mpSig.WriteString(strconv.FormatInt(getBalance.TS, 10))
	//目前部分区服，所以只有一个服
	mpSig.WriteString("&zone_id=1")

	//uri+secret
	mpSig.WriteString("&org_loc=/cgi-bin/midas/sandbox/getbalance&method=POST&session_key=")
	mpSig.WriteString(sessionKey)

	mpSigHMacSha256 := core.GetHmacCode(mpSig.String(), sessionKey)
	logs.Info("MidasGetBalance mpSigHMacSha256 [%s]", mpSigHMacSha256)

	getBalance.MpSig = mpSigHMacSha256

	logs.Info("getBalance %+v", getBalance)

	var url strings.Builder
	url.WriteString(`https://api.weixin.qq.com/cgi-bin/midas/sandbox/getbalance?access_token=`)
	url.WriteString(accessToken)

	sendBody, err := json.Marshal(getBalance)
	if err != nil {
		return nil, err
	}

	logs.Info("sendBody %s", string(sendBody))
	logs.Info("url %s", url.String())

	//创建请求
	postReq, err := http.Post(url.String(), "application/json; encoding=utf-8", strings.NewReader(string(sendBody)))
	if err != nil {
		return nil, err
	}

	body, _ := ioutil.ReadAll(postReq.Body)
	logs.Info("postReq [%s]", string(body))

	returnSt := GetBalanceReturnSt{}
	err = json.Unmarshal(body, &returnSt)
	if err != nil {
		return nil, err
	}

	if returnSt.Errcode != 0 {
		return nil, fmt.Errorf("errcode %d errmsg %s", returnSt.Errcode, returnSt.Errmsg)
	}
	return &returnSt, nil
}

//米大师支付结构
type MidasPaySt struct {
	//继承获取余额才结构
	GetBalance

	//amount 扣除游戏币数量，不能为 0
	Amt int64 `json:"amt"`
	//订单号，业务需要保证全局唯一；相同的订单号不会重复扣款。长度不超过63，只能是数字、大小写字母_-
	BillNo string `json:"bill_no"`
	//道具名称
	PayItem string `json:"pay_item"`
	//备注。会写到账户流水
	AppRemark string `json:"app_remark"`
}

//购买的何种道具，需要
type BuyItem struct {
	//amount 扣除游戏币数量，不能为 0
	Amt int64 `json:"amt"`
	//道具名称
	PayItem string `json:"pay_item"`
	//备注。会写到账户流水
	AppRemark string `json:"app_remark"`
}

//米大师支付返回结构
type MidasPayReturnSt struct {
	//错误码
	Errcode int `json:"errcode"`
	//错误信息
	Errmsg string `json:"errmsg"`
	//预扣后的余额
	Balance int64 `json:"balance"`
	///订单号，有效期是 48 小时
	BillNo string `json:"bill_no"`
	//本次扣的赠送币的余额
	UsedGenAmt int64 `json:"used_gen_amt"`
}

//米大师支付
func MidasPay(openid, sessionKey, gameName string, buyItem BuyItem) (*MidasPayReturnSt, error) {

	if buyItem.Amt <= 0 {
		return nil, fmt.Errorf("Amt is error :%d", buyItem.Amt)
	}
	accessToken := core.GetAccessToken(gameName)
	wxConf := core.GetWXConf(gameName)
	pay := MidasPaySt{}

	pay.AppId = wxConf.AppId
	pay.OfferId = wxConf.OfferId
	pay.PF = wxConf.PF
	pay.OpenId = openid
	pay.TS = time.Now().Unix()
	pay.ZoneId = "1"
	//pay.AccessToken = accessToken
	bill, err := idgenerate.BillNoID()
	if err != nil {
		return nil, err
	}
	pay.BillNo = strconv.FormatInt(bill, 10)
	pay.Amt = buyItem.Amt
	pay.AppRemark = buyItem.AppRemark
	pay.PayItem = buyItem.PayItem

	var sig strings.Builder
	//amt
	sig.WriteString("amt=")
	sig.WriteString(strconv.FormatInt(pay.Amt, 10))
	//app_remark
	sig.WriteString("&app_remark=")
	sig.WriteString(pay.AppRemark)

	//appid
	sig.WriteString("&appid=")
	sig.WriteString(pay.AppId)
	//bill_no
	sig.WriteString("&bill_no=")
	sig.WriteString(pay.BillNo)

	//米大师的id
	sig.WriteString("&offer_id=")
	sig.WriteString(pay.OfferId)
	//openid
	sig.WriteString("&openid=")
	sig.WriteString(pay.OpenId)
	//pay_item
	sig.WriteString("&pay_item=")
	sig.WriteString(pay.PayItem)

	//pf 平台标志
	sig.WriteString("&pf=")
	sig.WriteString(pay.PF)
	//时间戳
	sig.WriteString("&ts=")
	sig.WriteString(strconv.FormatInt(pay.TS, 10))
	//目前部分区服，所以只有一个服
	sig.WriteString("&zone_id=1")
	//uri+secret
	sig.WriteString("&org_loc=/cgi-bin/midas/sandbox/pay&method=POST&secret=")
	sig.WriteString(wxConf.MidasKey)

	logs.Info("sig %s", sig.String())

	//获取哈希
	sigHMacSha256 := core.GetHmacCode(sig.String(), wxConf.MidasKey)
	logs.Info("MidasGetBalance sigHMacSha256 [%s]", sigHMacSha256)

	pay.Sig = sigHMacSha256

	var mpSig strings.Builder
	mpSig.WriteString("access_token=")
	mpSig.WriteString(accessToken)

	//amt
	mpSig.WriteString("&amt=")
	mpSig.WriteString(strconv.FormatInt(pay.Amt, 10))
	//app_remark
	mpSig.WriteString("&app_remark=")
	mpSig.WriteString(pay.AppRemark)

	mpSig.WriteString("&appid=")
	mpSig.WriteString(pay.AppId)
	//bill_no
	mpSig.WriteString("&bill_no=")
	mpSig.WriteString(pay.BillNo)

	mpSig.WriteString("&offer_id=")
	mpSig.WriteString(pay.OfferId)

	mpSig.WriteString("&openid=")
	mpSig.WriteString(pay.OpenId)

	//pay_item
	mpSig.WriteString("&pay_item=")
	mpSig.WriteString(pay.PayItem)

	mpSig.WriteString("&pf=")
	mpSig.WriteString(pay.PF)

	mpSig.WriteString("&sig=")
	mpSig.WriteString(sigHMacSha256)

	mpSig.WriteString("&ts=")
	mpSig.WriteString(strconv.FormatInt(pay.TS, 10))
	//目前部分区服，所以只有一个服
	mpSig.WriteString("&zone_id=1")

	//uri+secret
	mpSig.WriteString("&org_loc=/cgi-bin/midas/sandbox/pay&method=POST&session_key=")
	mpSig.WriteString(sessionKey)

	logs.Info("mpSig %s", mpSig.String())

	mpSigHMacSha256 := core.GetHmacCode(mpSig.String(), sessionKey)
	logs.Info("MidasGetBalance mpSigHMacSha256 [%s]", mpSigHMacSha256)

	pay.MpSig = mpSigHMacSha256

	var url strings.Builder
	url.WriteString(`https://api.weixin.qq.com/cgi-bin/midas/sandbox/pay?access_token=`)
	url.WriteString(accessToken)

	sendBody, err := json.Marshal(pay)
	if err != nil {
		return nil, err
	}

	logs.Info("sendBody %s", string(sendBody))
	logs.Info("url %s", url.String())

	//创建请求
	postReq, err := http.Post(url.String(), "application/json; encoding=utf-8", strings.NewReader(string(sendBody)))
	if err != nil {
		return nil, err
	}

	body, _ := ioutil.ReadAll(postReq.Body)
	logs.Info("postReq [%s]", string(body))

	returnSt := MidasPayReturnSt{}
	err = json.Unmarshal(body, &returnSt)
	if err != nil {
		return nil, err
	}

	if returnSt.Errcode != 0 {
		return nil, fmt.Errorf("errcode %d errmsg %s", returnSt.Errcode, returnSt.Errmsg)
	}

	return &returnSt, nil
}

type MidasPresentSt struct {
	GetBalance
	//订单号，业务需要保证全局唯一；相同的订单号不会重复扣款。长度不超过63，只能是数字、大小写字母_-
	BillNo string `json:"bill_no"`
	//赠送游戏币的个数，不能为0
	PresentCounts int64 `json:"present_counts"`
}

type MidasPresentReutnSt struct {
	Errcode int    `json:"errcode"`
	Errmsg  string `json:"errmsg"`
	Balance int64  `json:"balance"`
}

//米大师赠送游戏币
//code2Session 帐号
//count 数量
func MidasPresent(openid, sessionKey, gameName string, count int64) (*MidasPresentReutnSt, error) {
	if count <= 0 {
		return nil, fmt.Errorf("count can not <= 0")
	}
	present := MidasPresentSt{}
	accessToken := core.GetAccessToken(gameName)
	wxConf := core.GetWXConf(gameName)

	present.OpenId = openid
	present.AppId = wxConf.AppId
	present.OfferId = wxConf.OfferId
	present.TS = time.Now().Unix()
	present.ZoneId = "1"
	present.PF = wxConf.PF
	bill, err := idgenerate.BillNoID()
	if err != nil {
		return nil, err
	}
	present.BillNo = strconv.FormatInt(bill, 10)
	present.PresentCounts = count

	sigMap := make(map[string]interface{})
	sigMap["openid"] = present.OpenId
	sigMap["appid"] = present.AppId
	sigMap["offer_id"] = present.OfferId
	sigMap["ts"] = strconv.FormatInt(present.TS, 10)
	sigMap["zone_id"] = present.ZoneId
	sigMap["pf"] = present.PF
	sigMap["bill_no"] = present.BillNo
	sigMap["present_counts"] = strconv.FormatInt(present.PresentCounts, 10)

	sigStr := analyzeSigMap(sigMap)
	var sigBuilder strings.Builder
	sigBuilder.WriteString(sigStr)
	//uri+secret
	sigBuilder.WriteString("&org_loc=/cgi-bin/midas/sandbox/present&method=POST&secret=")
	sigBuilder.WriteString(wxConf.MidasKey)
	sigStr = sigBuilder.String()

	logs.Info("sigStr %s", sigStr)

	//获取sig哈希
	sigHMacSha256 := core.GetHmacCode(sigStr, wxConf.MidasKey)
	sigMap["sig"] = sigHMacSha256
	sigMap["access_token"] = accessToken

	mpSigStr := analyzeSigMap(sigMap)
	var mpSigBuilder strings.Builder
	mpSigBuilder.WriteString(mpSigStr)
	//uri+secret
	mpSigBuilder.WriteString("&org_loc=/cgi-bin/midas/sandbox/present&method=POST&session_key=")
	mpSigBuilder.WriteString(sessionKey)
	mpSigStr = mpSigBuilder.String()

	logs.Info("mpSigStr %s", mpSigStr)
	//获取mpSig哈希
	mpSigHMacSha256 := core.GetHmacCode(mpSigStr, sessionKey)

	present.Sig = sigHMacSha256
	present.MpSig = mpSigHMacSha256

	var url strings.Builder
	url.WriteString(`https://api.weixin.qq.com/cgi-bin/midas/sandbox/present?access_token=`)
	url.WriteString(accessToken)

	sendBody, err := json.Marshal(present)
	if err != nil {
		return nil, err
	}

	logs.Info("sendBody %s", string(sendBody))
	logs.Info("url %s", url.String())

	//创建请求
	postReq, err := http.Post(url.String(), "application/json; encoding=utf-8", strings.NewReader(string(sendBody)))
	if err != nil {
		return nil, err
	}

	body, _ := ioutil.ReadAll(postReq.Body)
	logs.Info("postReq [%s]", string(body))
	returnSt := MidasPresentReutnSt{}
	err = json.Unmarshal(body, &returnSt)
	if err != nil {
		return nil, err
	}

	if returnSt.Errcode != 0 {
		return nil, fmt.Errorf("errcode %d errmsg %s", returnSt.Errcode, returnSt.Errmsg)
	}
	return &returnSt, nil
}

//解析米大师签名的map
func analyzeSigMap(sigMap map[string]interface{}) string {
	var arr []string
	for k := range sigMap {
		arr = append(arr, k)
	}

	sort.Strings(arr)

	var keyValueConcat []string

	for _, v := range arr {
		keyValueConcat = append(keyValueConcat, fmt.Sprintf("%s=%s", v, sigMap[v]))
	}
	keyValueStr := strings.Join(keyValueConcat, "&")
	return keyValueStr
}
